//  
//  Copyright © 2009 Jiří Zárevúcky <zarevucky.jiri@gmail.com>
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
// 
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
// 

using System;

using Gtk;
using Glade;

using Anculus.Core;

using L=Galaxium.Protocol.Xmpp.Library;
using Galaxium.Protocol.Xmpp.Library;
using Galaxium.Protocol.Xmpp.Library.Core;
using Galaxium.Protocol.Xmpp.Library.Extensions;

namespace Galaxium.Protocol.Xmpp.GtkGui
{
	public class RegisterDialog
	{
		#region widgets
		[Widget] protected Dialog dialog;
		
		[Widget] protected Notebook notebook;

		[Widget] protected ProgressBar progressbar;

		[Widget] protected Label error_label;

		[Widget] protected Button clear_button;
		[Widget] protected Button unregister_button;
		[Widget] protected Button cancel_button;
		[Widget] protected Button ok_button;
		[Widget] protected Button close_button;

		[Widget] protected Alignment form_wrapper;

		#endregion

		private System.Action _ok_action;
		private System.Action _cancel_action;
		private System.Action _clear_action;
		
		private L.Client _client;
		private JabberID _jid;
		private Registration _reg_object;

		private enum Tabs { Progress, Form, RemoveConfirm, Error, Success, CancelSuccess }
		
		private enum RegAction { Register, Unregister, Receive }
		
		public RegisterDialog (L.Client client, JabberID jid)
		{
			var gxml = new XML (null, "RegisterDialog.glade", null, null);
			gxml.Autoconnect (this);

			_client = client;
			_jid = jid;
			
			ok_button.Clicked += (s, e) => _ok_action ();
			close_button.Clicked += (s, e) => dialog.Destroy ();
			cancel_button.Clicked += (s, e) => _cancel_action ();
			dialog.Destroyed += HandleDestroyed;
			clear_button.Clicked += (s, e) => _clear_action ();
			unregister_button.Clicked += HandleUnregisterClicked;
		
			GLib.Timeout.Add (40, delegate () {
				if (progressbar == null) return false;
				progressbar.Pulse ();
				return true;
			});

			Retrieve();
			
			dialog.Show ();
		}

		private void Retrieve ()
		{
			DisplayWait (true);
			
			_reg_object = new Registration (_client, _jid);
			_reg_object.FormReceived += HandleFormReceived;
			_reg_object.CancelSucceeded += HandleCancelSucceeded;
			_reg_object.Failed += HandleFailed;
			_reg_object.RetrieveForm ();
		}

		void HandleFailed (object sender, StanzaErrorEventArgs e)
		{
			Application.Invoke (delegate {
				DisplayError (e.Error);
			});
		}

		void HandleCancelSucceeded (object sender, EventArgs e)
		{
			Application.Invoke (delegate {
				notebook.Page = (int) Tabs.CancelSuccess;
				CloseOnly ();
			});
		}

		void HandleFormReceived (object sender, DataFormEventArgs e)
		{
			Application.Invoke (delegate {
				Log.Info ("Creating form.");
				var form_widget = new DataFormWidget (_client, e.Form);
				form_wrapper.Add (form_widget);
				form_wrapper.ShowAll ();
						
				notebook.Page = (int) Tabs.Form;

				_ok_action = () => {
					var form = e.Form;
					form.SubmitSucceeded += (s, args) => DisplayRegistered ();
					form.SubmitFailed += (s, args) => DisplayError (args.Error);
					form.Submit ();
					DisplayWait (false);
				};

				_cancel_action = () => dialog.Destroy ();

				_clear_action = () => form_widget.Clear ();

				close_button.Hide ();
				clear_button.Show ();
				dialog.ActionArea.SetChildSecondary (clear_button, true);
				unregister_button.Show ();
				unregister_button.Sensitive = _reg_object.IsRegistered;
				ok_button.Show ();
				cancel_button.Show ();
			});
		}

		private void DisplayRegistered ()
		{
			Application.Invoke (delegate {
				notebook.Page = (int) Tabs.Success;
				CloseOnly ();
			});
		}

		private void HandleUnregisterClicked (object sender, EventArgs e)
		{
			notebook.Page = (int) Tabs.RemoveConfirm;

			var old_ok = _ok_action;
			
			_ok_action = () => {
				DisplayWait (false);
				_reg_object.Cancel ();
			};

			_cancel_action = () => {              // restore the form
				notebook.Page = (int) Tabs.Form;
				clear_button.Show ();
				unregister_button.Show ();
				_ok_action = old_ok;
				_cancel_action = () => dialog.Destroy ();
			};

			clear_button.Hide ();
			unregister_button.Hide ();
		}

		private void HandleDestroyed (object sender, EventArgs e)
		{
			_reg_object.CancelSucceeded -= HandleCancelSucceeded;
			_reg_object.Failed -= HandleFailed;
			_reg_object.FormReceived -= HandleFormReceived;
			_reg_object = null;
			progressbar = null;
		}

		private void DisplayWait (bool can_cancel)
		{
			notebook.Page = (int) Tabs.Progress;
			_cancel_action = () => dialog.Destroy ();
			clear_button.Hide ();
			unregister_button.Hide ();
			ok_button.Hide ();
			close_button.Hide ();
			cancel_button.Show ();
			cancel_button.Sensitive = can_cancel;
		}

		private void DisplayError (StanzaError error)
		{
			Application.Invoke (delegate {
				notebook.Page = (int) Tabs.Error;
				CloseOnly ();
				var text = error == null ? "Disconnected" : error.GetHumanRepresentation ();
				if (error != null && error.Description != null)
					text += ": " + error.Description;
				error_label.Text = text;
			});
		}

		private void CloseOnly ()
		{
			clear_button.Hide ();
			unregister_button.Hide ();
			cancel_button.Hide ();
			ok_button.Hide ();
			close_button.Show ();
		}
	}
}
